//
//  Node.hxx
//  WiFiMACSim
//
//  Created by Christopher Kemsley on 4/1/12.
//  Copyright (c) 2012 iAppolopolis. All rights reserved.
//

#ifndef WiFiMACSim_Node_hxx
#define WiFiMACSim_Node_hxx



/* Initialization */

template <typename DerivedClass>
StateMachineNode<DerivedClass> :: StateMachineNode ( ID const & node , UInteger startState )
:	Node ( node )
,	mState ( startState )
,	mCallbacks()
,	mNames()
	{
	
	}

template <typename DerivedClass>
void	StateMachineNode<DerivedClass> :: registerStateCallback	( UInteger stateID , UpdateMethod method , std::string const & name )
	{
	mCallbacks [stateID] = method ;
	mNames [stateID] = name ;
	}



/* Queries */

template <typename DerivedClass>
UInteger	StateMachineNode<DerivedClass> :: state ()
	{
	return mState ;
	}
template <typename DerivedClass>
void		StateMachineNode<DerivedClass> :: setState ( UInteger inState )
	{
	//std::cout << NodeStamp << "setState from '" << mNames[mState] << "' to '" << mNames[inState] << "'\n" ;
	mState = inState ;
	}



template <typename DerivedClass>
void	StateMachineNode<DerivedClass> :: update	( UInteger currentTime , Medium::MsgSet const & currentReception )
	{
	typename std::map<UInteger,UpdateMethod>::iterator found = mCallbacks . find ( mState ) ;
	if ( found == mCallbacks.end() )
		{
		ThrowException ( "StateMachineNode" , "State "<<mState<<" not mapped in "<<(typeid(DerivedClass).name()) , __FILE__,__LINE__ ) ;
		}
	DerivedClass * self = (DerivedClass*) this ;
	(self ->* (found->second) ) ( currentTime , currentReception ) ;
	}










/* Initialization */

template <typename DerivedClass>
TransitionalStateMachineNode<DerivedClass> :: TransitionalStateMachineNode ( ID const & node , UInteger startState )
:	Node ( node )
,	mState ( startState )
,	mRXMessage(0)
,	mRXStatus ( kIdle )
,	mCallbacks()
,	mNames()
	{
	
	}

template <typename DerivedClass>
void	TransitionalStateMachineNode<DerivedClass> :: registerStateCallback	( UInteger stateID , UpdateMethod method , std::string const & name )
	{
	mCallbacks [stateID] = method ;
	mNames [stateID] = name ;
	}



/* Queries */

template <typename DerivedClass>
UInteger	TransitionalStateMachineNode<DerivedClass> :: state ()
	{
	return mState ;
	}
template <typename DerivedClass>
void		TransitionalStateMachineNode<DerivedClass> :: setState ( UInteger inState )
	{
	//std::cout << NodeStamp << "setState from '" << mNames[mState] << "' to '" << mNames[inState] << "'\n" ;
	mState = inState ;
	}




template <typename DerivedClass>
void		TransitionalStateMachineNode<DerivedClass> :: markMyMessagesAsFailed ( Medium::MsgSet const & currentReception )
	{
	Medium::MsgSet::const_iterator	h	=	currentReception.begin() ;
	Medium::MsgSet::const_iterator	e	=	currentReception.end() ;
	while ( h != e )
		{
		Message * m = *(h++) ;
		if ( m->target == identity )
			m -> status = Message::kStatusFailedToReceive ;
		}
	}



template <typename DerivedClass>
void	TransitionalStateMachineNode<DerivedClass> :: update	( UInteger currentTime , Medium::MsgSet const & currentReception )
	{
	// FIXME: SEE if any were sent by us! If so, use THAT!
	
	UInteger now = System::shared().currentSlot() ;
	
	DerivedClass * self = (DerivedClass*) this ;
	
	typename std::map<UInteger,UpdateMethod>::iterator found = mCallbacks . find ( mState ) ;
	if ( found == mCallbacks.end() )
		{
		ThrowException ( "TransitionalStateMachineNode" , "State "<<mState<<" not mapped in "<<(typeid(DerivedClass).name()) , __FILE__,__LINE__ ) ;
		}
	
	
	UInteger	rxCount		=	currentReception.size() ;
	
	if ( rxCount != 0 )
		{
		Medium::MsgSet::const_iterator	here	=	currentReception.begin() ;
		Medium::MsgSet::const_iterator	end		=	currentReception.end() ;
		while ( here != end )
			{
			Message * m = *(here++) ;
			if ( m->sender == identity )
				{
				bool	isCompleted	=	m->lastSlot<now ;
				(self ->* (found->second) ) ( m , isCompleted , true ) ;
				
				mRXStatus = kIdle ;
				mRXMessage = 0 ;
				return ;
				}
			}
		}
	
	
	Message	*	current		=	(rxCount!=0) ? (*(currentReception.begin())) : (0) ;
	
	switch ( mRXStatus )
		{
			case kIdle : {
				if ( rxCount == 1 )
					{
					if ( System::shared().currentSlot() != current->firstSlot )
						{
						mRXStatus = kError ;
						markMyMessagesAsFailed ( currentReception ) ;
						}
					else
						{
						mRXStatus = kOK ;
						mRXMessage = current ;
						}
					}
				else if ( rxCount > 1 )
					{
					mRXStatus = kError ;
					markMyMessagesAsFailed ( currentReception ) ;
					}
			} break ;
			
			case kOK : {
				if ( rxCount > 1 )
					{
					mRXStatus = kError ;
					mRXMessage = 0 ;
					markMyMessagesAsFailed ( currentReception ) ;
					}
				else if ( rxCount == 0 )
					{
					mRXStatus = kIdle ;
					mRXMessage = 0 ;
					}
			} break ;
			
			case kError :
			default : {
				if ( rxCount == 0 )
					{
					mRXStatus = kIdle ;
					mRXMessage = 0 ;
					}
				else
					{
					markMyMessagesAsFailed ( currentReception ) ;
					}
			} break ;
		}
	
	
	try
		{
		if ( mRXStatus == kOK )
			{
			bool	isCompleted	=	mRXMessage->lastSlot<now ;
			(self ->* (found->second) ) ( mRXMessage , isCompleted , mRXStatus!=kIdle ) ;
			if ( isCompleted )
				{
				mRXStatus = kIdle ;
				mRXMessage = 0 ;
				}
			}
		else
			{
			(self ->* (found->second) ) ( 0 , false , mRXStatus!=kIdle ) ;
			}
		}
	CatchException ( "TransitionalStateMachineNode" , "Exception in '"<<identity<<"' @ "<<System::shared().currentSlot() , __FILE__,__LINE__ ) ;
	}





#endif
